﻿namespace NorthernLights
{
    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Threading;
    using System.Xml.Linq;

    /// <summary>
    /// VersionManagement class.
    /// </summary>
    public class VersionManagement
    {
        #region Fields
        ////public const string VariableAppVersion = "NorthernLights_AppVersion";
        private const string ApplicationManifestFilename = "WMAppManifest.xml";
        private static ILogger logger = DebugLogger.Instance;
        #endregion

        #region Public methods
        /// <summary>
        /// Get application version from WMAppManifest file.
        /// </summary>
        /// <returns>The application version.</returns>
        public static Version GetVersion()
        {
            return GetVersionFromManifestFile();
        }

        /// <summary>
        /// Check for new version of application against a HTTP endpoint.
        /// </summary>
        /// <param name="uri">The HTTP endpoint to check against.</param>
        /// <param name="action">The action to execute when a new version is available.</param>
        public static void CheckForNewVersion(string uri, Action action)
        {
            CheckForNewVersion(uri, new Action<bool>(
                (result) =>
                {
                    if (result == true)
                    {
                        action();
                    }
                }));
        }

        /// <summary>
        /// Check for new version of application against a HTTP endpoint.
        /// </summary>
        /// <param name="uri">The HTTP endpoint to check against.</param>
        /// <param name="action">The action to execute when a new version is available.</param>
        public static void CheckForNewVersion(string uri, Action<bool> action)
        {
            if (string.IsNullOrEmpty(uri))
            {
                logger.Write("parameter 'uri' is not set.");
            }

            if (action == null)
            {
                logger.Write("parameter 'action' is not set.");
            }

            BackgroundWorker bgw = new BackgroundWorker();

            bgw.DoWork += (sender, e) =>
            {
                Version version = GetVersion();

                if (version != null)
                {
                    DoWebRequest(uri, version, action);
                }
                else
                {
                    logger.Write("CheckVersion: Unable to determine application version number.");
                }
            };

            bgw.RunWorkerAsync();
        }
        #endregion

        #region Private methods
        /// <summary>
        /// Get version from WMAppManifest file.
        /// </summary>
        /// <returns>The application version.</returns>
        private static Version GetVersionFromManifestFile()
        {
            try
            {
                XDocument doc = XDocument.Load(ApplicationManifestFilename);

                string version = doc.Descendants("App").First<XElement>().Attribute("Version").Value;

                if (!string.IsNullOrEmpty(version))
                {
                    Version result;

                    if (Version.TryParse(version, out result))
                    {
                        return result;
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Ex:" + ex.Message);
            }

            return default(Version);
        }

        private static void DoWebRequest(string uri, Version appVersion, Action<bool> action)
        {
            string id = "appversionrequest";

            Timer t = null;
            int timeout = 60; // in seconds

            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
                request.Accept = "*/*";
                request.AllowAutoRedirect = true;

                // disable caching
                // NOTE: this doesn't seem to be working. recommend adding timestamp to request.
                request.Headers[HttpRequestHeader.CacheControl] = "no-cache";
                request.Headers[HttpRequestHeader.Pragma] = "no-cache";

                t = new Timer(
                    state =>
                    {
                        if (string.Compare(state.ToString(), id, StringComparison.InvariantCultureIgnoreCase) == 0)
                        {
                            logger.Write("Timeout reached for connection [{0}], aborting download.", id);

                            request.Abort();
                            t.Dispose();
                        }
                    },
                    id,
                    timeout * 1000,
                    0);

                request.BeginGetResponse(
                    r =>
                    {
                        try
                        {
                            if (t != null)
                            {
                                t.Dispose();
                            }

                            var httpRequest = (HttpWebRequest)r.AsyncState;
                            var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);

                            if (httpResponse.StatusCode == HttpStatusCode.OK)
                            {
                                using (StreamReader reader = new StreamReader(httpResponse.GetResponseStream()))
                                {
                                    string response = reader.ReadToEnd();

                                    try
                                    {
                                        Version latestVersion;

                                        if (Version.TryParse(response, out latestVersion))
                                        {
                                            if (action != null)
                                            {
                                                logger.Write("Installed version v{0} vs. Latest version v{1}.", appVersion, latestVersion);

                                                try
                                                {
                                                    action(appVersion < latestVersion);
                                                }
                                                catch (Exception ex)
                                                {
                                                    logger.Write("Failed to run action! {0}.", ex.Message);
                                                }
                                            }
                                        }
                                    }
                                    catch
                                    {
                                    }
                                }
                            }
                            else
                            {
                                logger.Write("Error occured accessing endpoint: {0} [{1}].", httpResponse.StatusCode, uri);
                            }
                        }
                        catch
                        {
                            logger.Write("Error occured accessing endpoint: [{0}].", uri);
                        }
                    },
                    request);
            }
            catch
            {
            }
        }
        #endregion
    }
}
